#include "include/paddr.h"
#include "include/common.h"
#include "include/reg.h"
#include "include/utils.h"
#include <elf.h>
#include <stdlib.h>

iringbuf iring;
//存储符号表中的一行
typedef struct symtable{
	char name[32]; // func name, 32 should be enough
	paddr_t addr;
	unsigned char info;
	paddr_t size;
} symbol_table;
static symbol_table *symbol_tables = NULL; // dynamic allocated

void iringbuf_write(uint32_t pc, char* inst) {
  iring.pcs[iring.iring_wf] = pc;
  strncpy(iring.logbuf[iring.iring_wf], inst, 128);
  iring.iring_wf = (iring.iring_wf + 1) % 15;

  //当写赶上读时，读索引跟随写索引的下一个
  if (iring.iring_wf == iring.iring_rf)
    iring.iring_rf = (iring.iring_rf + 1) % 15;
}

void iringbuf_read(void){
  while (iring.iring_rf != iring.iring_wf) {
    if(iring.iring_rf + 1 == iring.iring_wf)
      printf("%s <--- The nearest inst\n",iring.logbuf[iring.iring_rf]);
    else
      printf("%s \n",iring.logbuf[iring.iring_rf]);
    iring.iring_rf = (iring.iring_rf + 1) % 15;
  }
}

int symbol_cnt = 0;
//read elf
void read_elf(const char *elf_file){
  if (elf_file == NULL)
    return ;
  // 打开ELF文件
  FILE *fp = fopen(elf_file, "rb");
  //assert(fp, "Can not open '%s'", elf_file);

  //Call Elf32_Ehdr and read Elf32_Ehdr
  Elf32_Ehdr elf_header;
  //Only read 1 Elf32_Ehdr member
  if (fread(&elf_header, sizeof(Elf32_Ehdr), 1, fp) <= 0) {
    fclose(fp);
    //Assert(1,"fail to read elf header");
  }
  //printf("e_phoff = %x\n",elf_header.e_phoff);
  //printf("e_shoff = %x\n",elf_header.e_shoff);

  //从文件的开头跳转到Section header
  fseek(fp, elf_header.e_shoff, SEEK_SET);
  //Call Elf32_Shdr and read Elf32_Shdr
  Elf32_Shdr strtab_header;
  //Find strtab————>type:STRTAB
  while(strtab_header.sh_type != SHT_STRTAB){
    if (fread(&strtab_header, sizeof(Elf32_Shdr), 1, fp) <= 0) {
      fclose(fp);
      //Assert(1,"fail to read strtab header");
    }
  }
  //printf("sh_idex = %d\n",strtab_header.sh_name);
  //printf("sh_type = %d\n",strtab_header.sh_type);
  //printf("sh_size = %d\n",strtab_header.sh_size);
  //printf("sh_offset = %x\n",strtab_header.sh_offset);

  //从文件的开头跳转到StrTab，并读取strtab的内容
  char *strtab = (char *)malloc(strtab_header.sh_size);
  fseek(fp, strtab_header.sh_offset, SEEK_SET);
  if (fread(strtab, strtab_header.sh_size, 1, fp) <= 0) {
    fclose(fp);
    //Assert(1,"fail to read strtab");
  }
  //puts(strtab);

  //Find symtab————>type:SYMTAB
  //从文件的开头跳转到Section header
  Elf32_Shdr symtab_header;
  fseek(fp, elf_header.e_shoff, SEEK_SET);
  while(symtab_header.sh_type != SHT_SYMTAB){
    if (fread(&symtab_header, sizeof(Elf32_Shdr), 1, fp) <= 0) {
      fclose(fp);
      //Assert(1,"fail to read symtab header");
    }
  }
  //printf("sh_idex = %d\n",symtab_header.sh_name);
  //printf("sh_type = %d\n",symtab_header.sh_type);
  //printf("sh_offset = %x\n",symtab_header.sh_offset);

  //Call Elf32_Sym
  Elf32_Sym symbol;
  
  size_t num_symbols = symtab_header.sh_size / symtab_header.sh_entsize;  //entry num
  symbol_tables = (symbol_table *)malloc(num_symbols * sizeof(symbol_table));
  //Find symbol
  fseek(fp, symtab_header.sh_offset, SEEK_SET);

  for(int i = 0; i < num_symbols; i++)
  {
    if (fread(&symbol, sizeof(Elf32_Sym), 1, fp) <= 0) {
      fclose(fp);
      //Assert(1,"fail to read symbol");
    }
    //Get name
    const char *name = strtab + symbol.st_name;
    //Judge st_info == STT_FUNC
    if (ELF32_ST_TYPE(symbol.st_info) == STT_FUNC) {
      strncpy(symbol_tables[symbol_cnt].name, name, sizeof(symbol_tables[symbol_cnt].name)-1);
      symbol_tables[symbol_cnt].addr = symbol.st_value;
      printf("addr = %x \n",symbol_tables[symbol_cnt].addr);
      symbol_tables[symbol_cnt].info = symbol.st_info;
      symbol_tables[symbol_cnt].size = symbol.st_size;
      symbol_cnt++;
		}
  }
  printf("Load Elf Success!! \n");
  fclose(fp);
  free(strtab);
}

int call_depth;
void ftrace_call (paddr_t pc , paddr_t dnpc){

  //printf("addr = %x \n",symbol_tables[0].addr);
  if (symbol_tables == NULL) return;
  for (int i = 0; i < symbol_cnt; i++) {
		if(symbol_tables[i].addr == dnpc)
    {
      printf("0x%08x:", pc);

      for(int k = 0; k < call_depth; k++) printf("  ");
      printf("call  [%s@0x%08x]\n", symbol_tables[i].name, symbol_tables[i].addr);

      call_depth++;
    }
	}
}

void ftrace_ret (paddr_t pc){

  //printf("addr = %x \n",symbol_tables[0].addr);
  if (symbol_tables == NULL) return;
  for (int i = 0; i < symbol_cnt; i++) {
		if(symbol_tables[i].addr <= pc && pc < symbol_tables[i].addr + symbol_tables[i].size)
    {
      printf("0x%08x:", pc);
      call_depth--;

      for(int k = 0; k < call_depth; k++) printf("  ");
      printf("ret   [%s@0x%08x]\n", symbol_tables[i].name, symbol_tables[i].addr);

      
    }
	}
}